'use strict';

const InvalidGrantError = require('oauth2-server/lib/errors/invalid-grant-error');

module.exports = app => {

  class ModelTest {
    constructor() {}
    getUser() { return {username:'test',password: '123456'} }
    getClient() { return {id:'my_app', redirectUris: 'http://baidu.com,http://127.0.0.1:7001/index', grants: ['password', 'authorization_code','client_credentials']}}
    getAccessToken() { 
        return {
            accessToken: 'access_token',
            accessTokenExpiresAt: new Date(new Date().getTime() + 60*60*1000),
            refreshToken: 'refresh_token',
            refreshTokenExpiresAt: new Date(new Date().getTime() + 60*60*1000),
            scope: 'scope',
            client: this.getClient(),
            user: this.getUser()
        }
    }
    getRefreshToken() {
        return {
            refreshToken: 'refresh_token',
            refreshTokenExpiresAt: new Date(new Date().getTime() + 60*60*1000),
            scope: 'scope',
            client: this.getClient(), // with 'id' property
            user: this.getUser()
          };
    }
    getAuthorizationCode() {
        return {
            code: 'authorization_code',
            expiresAt: new Date(new Date().getTime() + 60*60*1000),
            redirectUri: 'http://127.0.0.1:7001/index',
            scope: 'scope',
            client: this.getClient(), // with 'id' property
            user: this.getUser()
        };
    }
  }
  /**
   * 参考 https://oauth2-server.readthedocs.io/en/latest/model/spec.html#
   */
  class Model {
    constructor(ctx) {
        ctx.logger.info('Model.constructor');
        this.ctx = ctx;
    }

    getModelTest(){
        return new ModelTest();
    }
    
    async getClient(clientId, clientSecret) {
      this.ctx.logger.info(`'getClient info' ${clientId} ${clientSecret}`);
      const client = await this.ctx.model.Client.findByPk(clientId);
      if (client && client.clientSecret === clientSecret) {
        return {
              id: client.id,
              redirectUris: client.redirectUris,
              grants: client.grants.split(',')
          };
      }
    }
    async getUser(username, password) {
      this.ctx.logger.info(`'getUser info' ${username} ${password}`);
      const user = await this.ctx.model.User.findOne({ where: {username: username} });
      if(!user) throw new InvalidGrantError('帐号不存在');
      if(password !== user.password) throw new InvalidGrantError('密码错误');
      return user;
    }
    async getAccessToken(accessToken) {
      const token = await this.ctx.model.AccessToken.findByPk(accessToken);
      return {
        accessToken: token.id,
        accessTokenExpiresAt: token.expiresAt,
        scope: token.scope,
        client: await this.ctx.model.Client.findByPk(token.clientId), // with 'id' property
        user: await this.ctx.model.User.findByPk(token.userId)
      };
        // imaginary DB queries
        // db.queryAccessToken({access_token: accessToken})
        //   .then(function(token) {
        //     return Promise.all([
        //       token,
        //       db.queryClient({id: token.client_id}),
        //       db.queryUser({id: token.user_id})
        //     ]);
        //   })
        //   .spread(function(token, client, user) {
        //     return {
        //       accessToken: token.access_token,
        //       accessTokenExpiresAt: token.expires_at,
        //       scope: token.scope,
        //       client: client, // with 'id' property
        //       user: user
        //     };
        //   });
        // return this.getModelTest().getAccessToken();
    }
    async getRefreshToken(refreshToken) {
      this.ctx.logger.info(`'getRefreshToken info' ${refreshToken}`);
      const token = await this.ctx.model.RefreshToken.findByPk(refreshToken);
      return {
        refreshToken: token.id,
        refreshTokenExpiresAt: token.expiresAt,
        scope: token.scope,
        client: await this.ctx.model.Client.findByPk(token.clientId), // with 'id' property
        user: await this.ctx.model.User.findByPk(token.userId)
      };
        // imaginary DB queries
        // db.queryRefreshToken({refresh_token: refreshToken})
        //   .then(function(token) {
        //     return Promise.all([
        //       token,
        //       db.queryClient({id: token.client_id}),
        //       db.queryUser({id: token.user_id})
        //     ]);
        //   })
        //   .spread(function(token, client, user) {
        //     return {
        //       refreshToken: token.refresh_token,
        //       refreshTokenExpiresAt: token.expires_at,
        //       scope: token.scope,
        //       client: client, // with 'id' property
        //       user: user
        //     };
        //   });


        // return this.getModelTest().getRefreshToken();
      }
    async saveToken(token, client, user) {
      this.ctx.logger.info(`'saveToken info' ${JSON.stringify(token)} ${JSON.stringify(client)} ${JSON.stringify(user)}`);
      const accessToken = await this.ctx.model.AccessToken.create({
        id: token.accessToken,
        expiresAt: token.accessTokenExpiresAt,
        scope: token.scope,
        clientId: client.id,
        userId: user.id
      })
      const refreshToken = await this.ctx.model.RefreshToken.create({
        id: token.refreshToken,
        expiresAt: token.refreshTokenExpiresAt,
        scope: token.scope,
        clientId: client.id,
        userId: user.id
      })
      this.ctx.logger.info(`'saveToken info1' ${JSON.stringify(accessToken)} ${JSON.stringify(refreshToken)}`);
      return {
            accessToken: accessToken.id,
            accessTokenExpiresAt: accessToken.expiresAt,
            refreshToken: refreshToken.id,
            refreshTokenExpiresAt: refreshToken.expiresAt,
            scope: accessToken.scope,
            client: {id: accessToken.clientId},
            user: {id: accessToken.userId}
          };
        // imaginary DB queries
        // let fns = [
        //   db.saveAccessToken({
        //     access_token: token.accessToken,
        //     expires_at: token.accessTokenExpiresAt,
        //     scope: token.scope,
        //     client_id: client.id,
        //     user_id: user.id
        //   }),
        //   db.saveRefreshToken({
        //     refresh_token: token.refreshToken,
        //     expires_at: token.refreshTokenExpiresAt,
        //     scope: token.scope,
        //     client_id: client.id,
        //     user_id: user.id
        //   })
        // ];
        // return Promise.all(fns)
        //   .spread(function(accessToken, refreshToken) {
        //     return {
        //       accessToken: accessToken.access_token,
        //       accessTokenExpiresAt: accessToken.expires_at,
        //       refreshToken: refreshToken.refresh_token,
        //       refreshTokenExpiresAt: refreshToken.expires_at,
        //       scope: accessToken.scope,
        //       client: {id: accessToken.client_id},
        //       user: {id: accessToken.user_id}
        //     };
        //   });
        
        // return this.getModelTest().getAccessToken();
      }
    async revokeToken(token) {
      this.ctx.logger.info(`'revokeToken info' ${JSON.stringify(token)}`);
      const refreshToken = await this.ctx.model.RefreshToken.findByPk(token.refreshToken);
      return refreshToken.destroy();
        // imaginary DB queries
        // return db.deleteRefreshToken({refresh_token: token.refreshToken})
        //   .then(function(refreshToken) {
        //     return !!refreshToken;
        //   });
      }
    async getAuthorizationCode(authorizationCode) {
      this.ctx.logger.info(`'getAuthorizationCode info' ${authorizationCode}`);
      const code = await this.ctx.model.RefreshToken.findByPk(authorizationCode);
      return {
        code: code.id,
        expiresAt: code.expiresAt,
        redirectUri:code.redirectUri,
        scope: code.scope,
        client: await this.ctx.model.Client.findByPk(code.clientId), // with 'id' property
        user: await this.ctx.model.User.findByPk(code.userId)
      };
        // imaginary DB queries
        // db.queryAuthorizationCode({authorization_code: authorizationCode})
        //   .then(function(code) {
        //     return Promise.all([
        //       code,
        //       db.queryClient({id: code.client_id}),
        //       db.queryUser({id: code.user_id})
        //     ]);
        //   })
        //   .spread(function(code, client, user) {
        //     return {
        //       code: code.authorization_code,
        //       expiresAt: code.expires_at,
        //       redirectUri: code.redirect_uri,
        //       scope: code.scope,
        //       client: client, // with 'id' property
        //       user: user
        //     };
        //   });

        //let code = this.getModelTest().getAuthorizationCode();
        //code.code = authorizationCode;
        //return code;
      }
    async getUserFromClient(client) {
        // imaginary DB query
        //return db.queryUser({id: client.user_id});
        this.ctx.logger.info(`'getUserFromClient info' ${JSON.stringify(client)}`);
        return await this.ctx.model.User.findByPk(client.user_id);
        // return this.getModelTest().getUser();
      }
    async saveAuthorizationCode(code, client, user) {
      this.ctx.logger.info(`'saveAuthorizationCode info' ${JSON.stringify(code)} ${JSON.stringify(client)} ${JSON.stringify(user)}`);
      const authorizationCode = await this.ctx.model.AccessToken.create({
        id: code.authorizationCode,
        expiresAt: code.expiresAt,
        redirectUri: code.redirectUri,
        scope: code.scope,
        clientId: client.id,
        userId: user.id
      })
      return {
          authorizationCode: authorizationCode.id,
          expiresAt: authorizationCode.expiresAt,
          redirectUri: authorizationCode.redirectUri,
          scope: authorizationCode.scope,
          client: client,
          user: user
        };
        // imaginary DB queries
        // let authCode = {
        //   authorization_code: code.authorizationCode,
        //   expires_at: code.expiresAt,
        //   redirect_uri: code.redirectUri,
        //   scope: code.scope,
        //   client_id: client.id,
        //   user_id: user.id
        // };
        

        // return db.saveAuthorizationCode(authCode)
        //   .then(function(authorizationCode) {
        //     return {
        //       authorizationCode: authorizationCode.authorization_code,
        //       expiresAt: authorizationCode.expires_at,
        //       redirectUri: authorizationCode.redirect_uri,
        //       scope: authorizationCode.scope,
        //       client: {id: authorizationCode.client_id},
        //       user: {id: authorizationCode.user_id}
        //     };
        //   });
        // const authorizationCode = this.getModelTest().getAuthorizationCode();
        // return {
        //   authorizationCode: code.authorizationCode,
        //   expiresAt: code.expiresAt,
        //   redirectUri: 'http://www.baidu.com',
        //   scope: code.scope,
        //   client: {id: authorizationCode.client_id},
        //   user: {id: authorizationCode.user_id}
        // };
      }
    async revokeAuthorizationCode(code) {
      this.ctx.logger.info(`'revokeAuthorizationCode info' ${code}`);
      const authorizationCode = await this.ctx.model.RefreshToken.findByPk(code);
      return authorizationCode.destroy();
      // return !!code.code;
        // imaginary DB queries
        // return db.deleteAuthorizationCode({authorization_code: code.authorizationCode})
        //   .then(function(authorizationCode) {
        //     return !!authorizationCode;
        //   });
      }
    async verifyScope(token, scope) {
        if (!token.scope) {
          return false;
        }
        let requestedScopes = scope.split(' ');
        let authorizedScopes = token.scope.split(' ');
        return requestedScopes.every(s => authorizedScopes.indexOf(s) >= 0);
      }
  }  
  return Model;
};